#pragma once

__inline __m256 as_mm256(float v)
{
	return _mm256_broadcast_ss(&v);
}

union f32stream
{
	f32stream() { }
	f32stream(float v) 
	{
		data = as_mm256(v);
	}
	f32stream(const float v[8]) 
	{
		data = _mm256_loadu_ps(v);
	}
	f32stream(__m256 v) : data(v) { }
	f32stream(const f32stream& other) 
	{
		data = other.data;
	}
	f32stream operator+(f32stream b)
	{
		return _mm256_add_ps(data, b.data);
	}
	f32stream operator+=(f32stream b)
	{
		*this = *this + b;
		return *this;
	}
	f32stream operator-(f32stream b)
	{
		return _mm256_sub_ps(data, b.data);
	}
	f32stream operator*(f32stream b)
	{
		return _mm256_mul_ps(data, b.data);
	}
	f32stream operator*=(f32stream b)
	{
		*this = *this * b;
		return *this;
	}
	__m256 data;
	float f[8];
};
inline f32stream operator-(f32stream v)
{
	static f32stream neg_one = as_mm256(-1);
	return neg_one * v;
}
inline f32stream inverse(f32stream v) { return _mm256_rcp_ps(v.data); }
inline f32stream invsqrt(f32stream v) { return _mm256_rsqrt_ps(v.data); }
inline f32stream sqrt(f32stream v) { return _mm256_mul_ps(invsqrt(v).data, v.data) ; }

struct v3stream
{
	v3stream() { }
	v3stream(f32stream px, f32stream py, f32stream pz) : x(px), y(py), z(pz) { }
	v3stream(const v3stream& other) { x = other.x; y = other.y; z = other.z; }
	f32stream x;
	f32stream y;
	f32stream z;
	v3stream operator*(f32stream b)
	{
		return v3stream(x * b, y * b, z * b);
	}
	v3stream operator-(v3stream b)
	{
		return v3stream(x - b.x, y - b.y, z - b.z);
	}
	v3stream operator*(v3stream b)
	{
		return v3stream(x * b.x, y * b.x, z * b.z);
	}
	v3stream operator+(v3stream b)
	{
		return v3stream(x + b.x, y + b.x, z + b.z);
	}
	void normalize()
	{
		auto im = invsqrt(x * x + y * y + z * z);
		x *= im;
		y *= im;
		z *= im;
	}
};
inline f32stream dot(v3stream a, v3stream b)
{ 
	return a.x * b.x + a.y * b.y + a.z * b.z; 
}
inline f32stream inv_mag(v3stream a) 
{
	return invsqrt(dot(a, a));
}
inline f32stream mag(v3stream a) 
{ 
	return sqrt(dot(a, a));
}
//isn't very fast for some reason
inline void normalize(
	f32stream* __restrict x, 
	f32stream* __restrict y, 
	f32stream* __restrict z, 
	int block_count)
{
	auto __restrict xp = x;
	auto __restrict yp = y;
	auto __restrict zp = z;
	for(int i = 0; i < block_count; i++)
	{
		auto a = *xp; auto b = *yp; auto c = *zp;

		auto invmag = invsqrt(a * a + b * b + c * c);
		*xp = a * invmag;
		*yp = b * invmag;
		*zp = c * invmag;

		xp++;
		yp++;
		zp++;
	}
}